<?php
class QueueController extends Zend_Controller_Action {
	protected $_queue = array();
	protected $_resourceTypes = array();
	protected $_gameObjects	= array();

	/**
	 * Initialmethode die Aufgerufen wird, bevor eine Action ausgefuehrt wird
	 * @param none
	 * @return none
	 */
	public function init() {
		$this->_initQueue();
	}

	/**
	 * fragt bei jedem Aufruf der Queue den Status der enthaltenen Eintraege und verarbeitet
	 * sie mit dem aktuellen Timestamp
	 * @param none
	 * @return none
	 */
	protected function _initQueue() {
		$properties	= Zend_Registry::get('GameProperties');
		$model			= new Application_Model_Queue();
		$tstamp			= time();
		$modify			= $properties->getModified();

		if(is_array($data = $model->findall(null, array('Actions', 'QueueResources', 'QueueObjects')))) {
			$this->_queue = $data;

			foreach($this->_getQueueInteval($tstamp, $modify) as $period) {
				if(($element = $this->_currentQueueElement()) !== false) {
					$this->_processQueueElement($period, $element);
				}
			}
		}

		// -----------------------------------------------------------------------------------
		// Aufraeumarbeiten
		$mProperties	= new Application_Model_GameProperties();
		$properties->setModified($tstamp);
		$mProperties->save($properties);

		$mQueueUpdates = new Application_Model_QueueUpdates();
		$mQueueUpdates->remove(array('timestamp < ?' => $tstamp));
	}

	/**
	 * Verarbeitet ein Queue-Element innerhalb eines uebergebenen Zeitraums. Sollte ein Queue-
	 * Element vollstaendig abgearbeitet sein, wird das folgende Element verarbeitet
	 * @param int $allottedTime Zeitraum in Sekunden, welche dem Element zur Verarbeitung zugewiesen wurde
	 * @param object Application_Model_Schema_Queue aktuelles Element
	 */
	protected function _processQueueElement($allottedTime, $element) {
		$actionPlugin				= $this->_getActionPlugin($element->action->id);

		// falls das Plugin Resourcen benoetigt
		if(empty($this->_resourceTypes) === false) {
			$actionPlugin->setResourceData($this->_resourceTypes);
		}

		// falls das Plugin Objekte benoetigt
		if(empty($this->_gameObjects) === false) {
			$actionPlugin->setObjectData($this->_gameObjects);
		}

		// Ausfuehrungszeit berechnen
		$executionComplete	= false;
		$executionTime			= $actionPlugin->onResponse()
			->setElapsedTime($element->getElapsedTime())
			->getExecutionTime();

		// wenn die uebergebene Zeitspanne groesser ist als die berechnete Zeitspanne, verwende
		// die berechnete Zeitspanne und errechne was nach der Verarbeitung von der uebergebenen
		// Zeitspanne uebrig bleibt
		if($allottedTime > $executionTime) {
			$allottedTime	= $allottedTime - $executionTime;

		} else {
			$executionTime	= $allottedTime;
			$allottedTime		= 0;
		}

		// sollte eine Aktion komplett verarbeitet sein, entferne sie aus der Queue
		if($actionPlugin->onResponse()->execute($executionTime) === true) {
			$executionComplete = true;
			$this->_removeQueueElement($element);

		// bei Aktionen, die auf Zeitlaufen, sollte die neu gesetzte verwendete	Zeit in der
		// Queue gespeichert werden
		} elseif($element->getElapsedTime() !== null) {
			$element->setElapsedTime($actionPlugin->getElapsedTime());

			$mQueue = new Application_Model_Queue();
			$mQueue->save($element);
		}

		// sollte noch Verarbeitungszeit vorhanden sein, rufe das naechste Element auf
		if($allottedTime !== 0 && $executionComplete === true) {
			if(($element = $this->_currentQueueElement()) !== false) {
				$this->_processQueueElement($allottedTime, $element);
			}
		}

		return $allottedTime;
	}

	/**
	 * laedt ein Plugin vom Typ System_Plugin_Action_$name und liefert ein Instanz hiervon
	 * zurueck; sollte keine Plugin von diesem Typ existieren wird das Default-Plugin
	 * System_Plugin_Action_Default zurueckgeliefert
	 *
	 * @param int $id Name der Aktion in der Datenbank
	 * @param object Instanz auf ein Plugin vom Typ System_Plugin_Action
	 */
	protected function _getActionPlugin($id = null) {
		$pluginLoader = new Zend_Loader_PluginLoader();
		$pluginLoader->addPrefixPath('System_Plugin_Action_', APPLICATION_PATH . '/modules/system/plugin/action/');

		// in den meisten Faellen steht die Id der Aktion zur Verfuegung -> deswegen lade hier
		// die HID der Aktion
		// @todo: im spaeteren Verlauf sollte das Plugin aus dem Cache geladen werden, welcher
		// als Kennzeichnung die Aktion-Id beinhaltet
		$mAction	= new Application_Model_Actions();
		$dAction	= $mAction->find(array('actions.id = ?' => $id));

		// Versuche das Action-Plugin zu laden, im Fehlerfall wird das Default-Plugin verwendet
		// Das Default-Plugin muss immer geladen werden, da hier der Autoloader nicht verwendet
		// wird
		$pluginName = $pluginLoader->load('Default');

		try {
			$pluginName = $pluginLoader->load($dAction->hid);

		} catch(Zend_Loader_PluginLoader_Exception $e) {
			// hier gibt es eigentlich nichts abzufangen, da eh bereits das Default-Plugin
			// verwendet wird
		}

		$pluginObject = new $pluginName();
		return $pluginObject;
	}

	/**
	 * fuegt eine Aktion vom Typ CollectResource (Resourcen sammeln) der Queue hinzu
	 * @param none
	 * @erturn none
	 */
	protected function _addCollectResourceAction() {
		$pCollectResource = $this->_getActionPlugin((int) $this->getRequest()->getParam('actionId'));
		$pCollectResource->onInit((int) $this->getRequest()->getParam('sourceId'));

		return $pCollectResource;
	}

	/**
	 * fuegt eine Aktion vom Typ ReduceTileResource (wie z.B. Holz faellen) der Queue hinzu
	 * @param none
	 * @return none
	 */
	protected function _addReduceTileResourceAction() {
		$pCollectResource = $this->_getActionPlugin((int) $this->getRequest()->getParam('actionId'));
		$pCollectResource->onInit((int) $this->getRequest()->getParam('sourceId'));

		return $pCollectResource;
	}

	/**
	 * fuegt eine Aktion vom Typ CreateObject (Objekt erstellen) der Queue hinzu
	 * @param none
	 * @erturn none
	 */
	protected function _addCreateObjectAction() {
		$pCollectResource = $this->_getActionPlugin((int) $this->getRequest()->getParam('actionId'));
		$pCollectResource->onInit((int) $this->getRequest()->getParam('sourceId'));

		return $pCollectResource;
	}

	/**
	 * listet alle vorhandenen Queue-Eintraege, die eingetragen und noch nicht abgelaufen
	 * sind, auf
	 * @param none
	 * @return none
	 */
	public function indexAction() {
		$queueEndtimeTotal = 0;
		$queueCurTime			 = time();

//		foreach($this->_queue as &$queue) {
//			$methods = array();
//
//			if($queue->action->use->objects !== null || $queue->action->produce->objects !== null) {
//				$methods = array_merge($methods, $this->_config->method->object->toArray());
//			}
//			if($queue->action->use->resources !== null || $queue->action->produce->resources !== null) {
//				$methods = array_merge($methods, $this->_config->method->resource->toArray());
//			}
//
//			$queueEndtime				= $this->_processExpirationMethod($methods, $queue->action);
//			$queueEndtimeTotal	= $queueEndtimeTotal + $queueEndtime;
//			$queueEndtime				= $queueCurTime + floor($queueEndtimeTotal);
//			$queue->setEndTime($queueEndtime);
//		}

		$this->view->assign('data', $this->_queue);
	}

	/**
	 * fuegt einen neuen Eintrag in die Queue ein und ruft eine interne Methode vom Typ
	 * Parameter type auf
	 * @param none
	 * @return none
	 */
	public function addAction() {
		$method = '_add';
		$filter	= new Zend_Filter();

		$method .= $filter
			->addFilter(new Zend_Filter_Word_SeparatorToCamelCase('-'))
			->filter($this->getRequest()->getParam('type'));
		$method .= 'Action';

		if(method_exists($this, $method) && ($plugin = $this->$method()) !== null) {

			//----------------------------------------------------------------------------------
			// Plugin laden	und Daten (Aktion, Resourcen, Objekte) abgereifen
			$dAction		= $plugin->getActionData();
			$dResource	= $plugin->getResourceData();
			$dObject		= $plugin->getObjectData();

			//----------------------------------------------------------------------------------
			// neuen Eintrag fuer die Queue vorbereiten, mit Werten befuellen und speichern
			$mQueue	= new Application_Model_Queue();
			$dQueue	= new Application_Model_Schema_Queue();
			$tQueue	= time();

			$dQueue
				->setStartTime($tQueue)
				->setElapsedTime($plugin->getElapsedTime())
				->setAction($dAction);
			$dQueue->setId($mQueue->save($dQueue));

			//----------------------------------------------------------------------------------
			// Resourcen der Queue verarbeiten und abspeichern
			if(empty($dResource) === false) {
				$mQueueResource		= new Application_Model_QueueResources();

				foreach($dResource as $resource) {
					$dQueueResource = new Application_Model_Schema_QueueResource();
					$dQueueResource
						->setQueue($dQueue)
						->setResource($resource);

					$mQueueResource->save($dQueueResource);
				}
			}

			//----------------------------------------------------------------------------------
			// Objekte der Queue verarbeiten und abspeichern
			if(empty($dObject) === false) {
				$mGameObject	= new Application_Model_GameObjects();
				$mQueueObject = new Application_Model_QueueObjects();

				foreach($dObject as $dGameObject) {

					if($dGameObject->getId() === null) {
						$dGameObject->setId($mGameObject->save($dGameObject));
					}

					$dQueueObject = new Application_Model_Schema_QueueObject();
					$dQueueObject
						->setQueue($dQueue)
						->setGameObject($dGameObject);

					$mQueueObject->save($dQueueObject);
				}
			}
		}
	}





// ---------------------------------------------------------------------------------------
// DIESER TEIL GEHOERT INS QUEUE_UPDATES_MODEL AUSGELAGERT
// ---------------------------------------------------------------------------------------
		/**
	 * liefert eine Liste von Sekunden, nach deren Definition eine Neuberechnung der Queue-
	 * Eintraege stattfindet
	 * @todo Auswertung solle effizienter Ablaufen
	 * @param int $curTstamp aktueller Timestamp
	 * @param int $modifyTstamp Timestamp der letzten Berechnung
	 * @return type
	 */
	protected function _getQueueInteval($curTstamp, $modifyTstamp = null) {
		$intervalSet				= array();
		$intervalPrevEntry	= 0;
		$queueUpdateModel		= new Application_Model_QueueUpdates();
		$queueUpdateData		= $queueUpdateModel->findallPeriod(array('queue_updates.timestamp < ?' => $curTstamp));


		$queueUpdateData[]	= $curTstamp;
		sort($queueUpdateData);

		// -----------------------------------------------------------------------------------
		// Eintraege vor dem Modify (sollten eigentlich vorhanden sein!)
		for($i = 0; $n = count($queueUpdateData), $i < $n; $i++) {
			if($queueUpdateData[$i] < $modifyTstamp) {
				$intervalTmpEntry = $modifyTstamp - $queueUpdateData[$i];
				$intervalEntry		= $intervalTmpEntry;

				if($i !== 0) {
					$intervalEntry	= $intervalPrevEntry - $intervalTmpEntry;
				}

				$intervalPrevEntry	= $intervalTmpEntry;
				$intervalSet[]			= $intervalEntry;
			}
		}

		// -----------------------------------------------------------------------------------
		// Eintraege = Modify
		$intervalEntry			= $curTstamp - $modifyTstamp;
		$intervalPrevEntry	= $intervalEntry;

		// -----------------------------------------------------------------------------------
		// Eintraege nach dem Modify
		for($i = 0; $i < $n; $i++) {
			if($queueUpdateData[$i] > $modifyTstamp) {
				$intervalTmpEntry = $curTstamp - $queueUpdateData[$i];

				$intervalEntry		= $intervalPrevEntry - $intervalTmpEntry;

				$intervalPrevEntry	= $intervalTmpEntry;
				$intervalSet[]			= $intervalEntry;
			}
		}

		return $intervalSet;
	}
// ---------------------------------------------------------------------------------------
// AUSLAGERUNG ENDE
// ---------------------------------------------------------------------------------------





// ---------------------------------------------------------------------------------------
// DIESER TEIL GEHOERT INS QUEUE_MODEL AUSGELAGERT
// ---------------------------------------------------------------------------------------
	protected function _registerQueueElementResource($resource) {
		$this->_resourceTypes[$resource->resource->id] = $resource;
	}

	protected function _registerQueueElementObject($object) {
		$this->_gameObjects[$object->object->id] = $object;
	}

	protected function _currentQueueElement() {
		return $this->_registerQueueElement(current($this->_queue));
	}

	protected function _registerQueueElement($element = false) {
		if($element === false) {
			return $element;
		}

		foreach($element->resources as $resource) {
			$this->_registerQueueElementResource($resource);
		}

		foreach($element->objects as $object) {
			$this->_registerQueueElementObject($object);
		}

		return $element;
	}

	protected function _removeQueueElement($element) {
		unset($this->_queue[$element->id]);

		// -----------------------------------------------------------------------------------
		// Resourcen-Verknuepfungen entfernen
		if($element->resources !== null) {
			$queueResources = new Application_Model_QueueResources();
			$queueResources->remove(array('queue_id = ?' => $element->id));
		}

		// -----------------------------------------------------------------------------------
		// Objekt-Verknuepfungen entfernen
		if($element->objects !== null) {
			$mQueueObject = new Application_Model_QueueObjects();
			$mQueueObject->remove(array('queue_id = ?' => $element->id));
		}

		// -----------------------------------------------------------------------------------
		// Queue entfernen
		$mQueue = new Application_Model_Queue();
		$mQueue->remove(array('id = ?' => $element->id));
	}
// ---------------------------------------------------------------------------------------
// AUSLAGERUNG ENDE
// ---------------------------------------------------------------------------------------
}
?>